﻿#region Header

// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------

#endregion

#region Usings

using System;
using System.Net;
using System.Threading;

using ScrumTable.Common.Threading;

#endregion

namespace ScrumTable.Common.Security
{
    /// <summary>
    /// Represents the information needed in order to establish
    /// a connection to an external, windows based system.
    /// </summary>
    public class ConnectionInformation
    {
        #region Declarations

        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly ReaderWriterLock _backEndIdLock = new ReaderWriterLock();

        private const string DomainSeparator = "\\";
        private const string EMailSeparator = "@";

        private volatile string _backEndId;

        #endregion

        #region Properties

        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets a new global empty connection information instance.
        /// </summary>
        public static ConnectionInformation Empty
        {
            get { return new ConnectionInformation(string.Empty, string.Empty); }
        }

        /// <summary>
        /// Gets/sets the unique (back end) id of the underlying connection information.
        /// </summary>
        public string BackEndId
        {
            get { using (_backEndIdLock.AcquireReaderLock()) { return (_backEndId ?? string.Empty); } }
            set { using (_backEndIdLock.AcquireWriterLock()) { _backEndId = value; } }
        }

        /// <summary>
        /// Gets the network credentials which are passed to the end system.
        /// </summary>
        public ICredentials NetworkCredentials
        {
            get; private set;
        }

        /// <summary>
        /// Gets the original user name provided by the constructor.
        /// </summary>
        public string OriginalUserName { get; private set; }

        /// <summary>
        /// Gets the user name of the underlying connection information.
        /// </summary>
        public string UserName { get; private set; }

        /// <summary>
        /// Gets the domain name of the underlying connection information.
        /// </summary>
        public string Domain { get; private set; }

        /// <summary>
        /// Returns true if the password is null or empty.
        /// </summary>
        public bool IsPasswordEmpty { get; private set; }

        /// <summary>
        /// Returns true if the default network credentials is used for the
        /// connection.
        /// </summary>
        public bool IsDefaultCredential { get; private set; }

        /// <summary>
        /// Gets/sets the internal id of the current connection information.
        /// </summary>
        internal string InternalId { get; private set; }

        #endregion

        #region Constructors / Destructor

        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new ConnectionInformation instance with the given
        /// network credentials and points to the given end system uri.
        /// </summary>
        /// <param name="userName">User name of the user to log in (in a well ordered and unqiue fromat, for TFS for example AD_NAME\UserName).</param>
        /// <param name="password">Password of the user to log in.</param>
        public ConnectionInformation(string userName, string password)
        {
            InternalId = Guid.NewGuid().ToString("D");
            OriginalUserName = (userName ?? string.Empty);
            UserName = (userName ?? string.Empty);
            IsPasswordEmpty = string.IsNullOrEmpty(password);
            IsDefaultCredential = (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password));

            if (!IsDefaultCredential)
            {
                if (userName.Contains(DomainSeparator))
                {
                    string[] userNameDomain = userName.Split(DomainSeparator[0]);
                    UserName = userNameDomain[1];
                    Domain = userNameDomain[0];
                }
                else if (userName.Contains(EMailSeparator))
                {
                    string[] userNameEmail = userName.Split(EMailSeparator[0]);
                    UserName = userNameEmail[0];
                    Domain = userNameEmail[1];
                }
                else
                {
                    UserName = userName;
                }
            }

            NetworkCredential userPwCred;
            
            if (IsDefaultCredential)
                userPwCred = CredentialCache.DefaultNetworkCredentials;
            else if (!string.IsNullOrEmpty(Domain))
                userPwCred = new NetworkCredential(UserName, password, Domain);
            else
                userPwCred = new NetworkCredential(UserName, password);

            NetworkCredentials = new ConnectionInfoStore(userPwCred);
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0} [UserName: {1}, IsPasswordEmpty: {2}, IsDefaultCredential: {3}]",
                base.ToString(),
                UserName,
                IsPasswordEmpty,
                IsDefaultCredential);
        }

        #endregion



        //--------------------------------------------------------------------
        // Nested Classes
        //--------------------------------------------------------------------

        /// <summary>
        /// Contains a single credential for login purposes.
        /// </summary>
        private class ConnectionInfoStore : ICredentials, ICredentialsByHost
        {
            #region Declarations
            //--------------------------------------------------------------------
            // Declarations
            //--------------------------------------------------------------------

            private readonly NetworkCredential _defaultCred;

            #endregion

            #region Properties
            //--------------------------------------------------------------------
            // Properties
            //--------------------------------------------------------------------

            #endregion

            #region Constructors / Destructor
            //--------------------------------------------------------------------
            // Constructors / Destructor
            //--------------------------------------------------------------------

            internal ConnectionInfoStore(NetworkCredential cred)
            {
                PreCondition.AssertNotNull(cred, "cred");

                _defaultCred = cred;
            }

            #endregion

            #region Methods
            //--------------------------------------------------------------------
            // Methods
            //--------------------------------------------------------------------

            NetworkCredential ICredentials.GetCredential(Uri uri, string authType)
            {
                return _defaultCred;
            }

            NetworkCredential ICredentialsByHost.GetCredential(string host, int port, string authenticationType)
            {
                return _defaultCred;
            }

            #endregion

            #region Events
            //--------------------------------------------------------------------
            // Events
            //--------------------------------------------------------------------

            #endregion
        }
    }
}